﻿using Digitalmes.Application.Configuration;

namespace Digitalmes.Application.Mes;

/// <summary>
/// 工单数据更新服务。
/// </summary>
public sealed class WoIncrementService : IApplicationService
{
    private readonly SugarRepository<ProdWo> _woRepo;
    private readonly SugarRepository<ProdWoSchedule> _woScheduleRepo;
    private readonly SugarRepository<ProdTransSn> _transSnRepo;

    private readonly ApplicationConfig _appConfig;

    public WoIncrementService(SugarRepository<ProdWo> woRepo,
        SugarRepository<ProdWoSchedule> woScheduleRepo,
        SugarRepository<ProdTransSn> transSnRepo,
        IOptions<ApplicationConfig> appConfigOptions)
    {
        _woRepo = woRepo;
        _woScheduleRepo = woScheduleRepo;
        _transSnRepo = transSnRepo;
        _appConfig = appConfigOptions.Value;
    }

    /// <summary>
    /// 获取当前进行或准备要进行的工单，没有找到返回 null。
    /// </summary>
    /// <param name="productId">产品 Id</param>
    /// <returns></returns>
    public async Task<ProdWo?> GetCurrentWoAsync(long? productId)
    {
        bool isFixedWorkOrder = _appConfig.WorkOrder.IsFixed; // 固定工单
        bool isAutoCompleteAndSwitch = _appConfig.WorkOrder.IsAutoCompleteAndSwitch; // 自动切换

        // 找到默认工单
        // 查找当前排序最靠前的、且处于 "已下发" 和 "执行中" 状态的工单
        WoStatusEnum[] woStatuses = [WoStatusEnum.Executing, WoStatusEnum.Issued];
        var woSchedule = await _woScheduleRepo.AsQueryable()
            .Includes(s => s.WorkOrder, w => w!.Product)
            .Where(s => woStatuses.Contains(s.WorkOrder!.Status))
            .WhereIF(isFixedWorkOrder, s => s.WorkOrder!.ProductId == productId) // 若是固定工单
            .WhereIF(isAutoCompleteAndSwitch, s => s.WorkOrder!.OnlineQty < s.WorkOrder.TotalQty) // 自动切换工单，上线数量等于总数量时自动切换
            .OrderBy(s => s.Sort)
            .FirstAsync();

        return woSchedule?.WorkOrder;
    }

    /// <summary>
    /// 产品上线，更新工单
    /// </summary>
    /// <param name="workOrder">工单</param>
    /// <returns></returns>
    public async Task ProductOnlineIncrementAsync(ProdWo workOrder)
    {
        if (workOrder.Status == WoStatusEnum.Issued)
        {
            workOrder.ActualStartTime = DateTime.Now;
            workOrder.SetStatus(WoStatusEnum.Executing); // "已下发" 状态切换为 "生产中"。
        }

        workOrder.OnlineQty += 1;
        await _woRepo.UpdateAsync(workOrder, s => new
        {
            s.ActualStartTime,
            s.Status,
            s.LastStatus,
            s.OnlineQty,
            s.UpdateTime,
        });
    }

    /// <summary>
    /// 离线返工完工，工单更新。
    /// </summary>
    /// <param name="wo">工单单号</param>
    /// <param name="needRelaunch">是否需要重新上线，若为 false，会将产品作为下线完工来更新工单</param>
    /// <returns></returns>
    public async Task OfflineReworkIncrementAsync(string wo, bool needRelaunch)
    {
        var workOrder = await _woRepo.GetFirstAsync(s => s.Wo == wo);
        if (workOrder == null)
        {
            return;
        }

        workOrder.UnqualifiedQty -= 1; // 不合格质量-1

        if (!needRelaunch)
        {
            workOrder.QualifiedQty += 1;
            workOrder.CompletedQty += 1;

            // 单据自动完工并切换
            if (_appConfig.WorkOrder.IsAutoCompleteAndSwitch)
            {
                if (workOrder.CompletedQty == workOrder.TotalQty)
                {
                    workOrder.SetStatus(WoStatusEnum.Completed);
                    workOrder.ActualEndTime = DateTime.Now;
                }
            }
        }

        await _woRepo.UpdateAsync(workOrder, s => new
        {
            s.ActualEndTime,
            s.Status,
            s.LastStatus,
            s.CompletedQty,
            s.QualifiedQty,
            s.UnqualifiedQty,
            s.UpdateTime,
        });
    }

    /// <summary>
    /// 在线返工完成，工单更新。
    /// </summary>
    /// <param name="wo">工单单号</param>
    /// <param name="snStatus">SN 状态</param>
    /// <param name="isCompleted">是否是完工下线</param>
    /// <returns></returns>
    public async Task OnlineReworkIncrementAsync(string wo, SnStatusEnum snStatus, bool isCompleted)
    {
        var workOrder = await _woRepo.GetFirstAsync(s => s.Wo == wo);
        if (workOrder == null)
        {
            return;
        }

        bool isChanged = false;

        // 只有在 SN 合格时，才去将工单中的不合格数量-1。
        if (snStatus == SnStatusEnum.OK)
        {
            workOrder.UnqualifiedQty -= 1;

            isChanged = true;
        }

        // 完工下线，SN 一定是 OK 状态
        if (isCompleted)
        {
            workOrder.QualifiedQty += 1;
            workOrder.CompletedQty += 1;

            if (_appConfig.WorkOrder.IsAutoCompleteAndSwitch)
            {
                if (workOrder.CompletedQty == workOrder.TotalQty)
                {
                    workOrder.SetStatus(WoStatusEnum.Completed);
                    workOrder.ActualEndTime = DateTime.Now;
                }
            }

            isChanged = true;
        }

        if (isChanged)
        {
            await _woRepo.UpdateAsync(workOrder, s => new
            {
                s.ActualEndTime,
                s.Status,
                s.LastStatus,
                s.CompletedQty,
                s.QualifiedQty,
                s.UnqualifiedQty,
                s.UpdateTime,
            });
        }
    }

    /// <summary>
    /// 线上 NG 或下件，工单更新。
    /// </summary>
    /// <param name="wo">工单单号</param>
    /// <param name="snStatus">SN 状态</param>
    /// <param name="isCompleted">是否已完工，只有 OK 状态才能完工</param>
    /// <returns></returns>
    public async Task OnlineIncrementAsync(string wo, SnStatusEnum snStatus, bool isCompleted)
    {
        // 在产品 NG 或是 下线 时才更新数量（NG 后会下线）
        if (snStatus is not SnStatusEnum.NG && !isCompleted)
        {
            return;
        }

        var workOrder = await _woRepo.GetFirstAsync(s => s.Wo == wo);
        if (workOrder == null)
        {
            return;
        }

        bool isChanged = false;

        if (snStatus == SnStatusEnum.NG)
        {
            workOrder.UnqualifiedQty += 1;

            isChanged = true;
        }
     
        // 完工（NG 品不能直接完工）
        if (isCompleted)
        {
            workOrder.QualifiedQty += 1;
            workOrder.CompletedQty += 1;

            if (_appConfig.WorkOrder.IsAutoCompleteAndSwitch)
            {
                if (workOrder.CompletedQty == workOrder.TotalQty)
                {
                    workOrder.SetStatus(WoStatusEnum.Completed);
                    workOrder.ActualEndTime = DateTime.Now;
                }
            }

            isChanged = true;
        }

        if (isChanged)
        {
            await _woRepo.UpdateAsync(workOrder, s => new
            {
                s.ActualEndTime,
                s.Status,
                s.LastStatus,
                s.CompletedQty,
                s.QualifiedQty,
                s.UnqualifiedQty,
                s.UpdateTime,
            });
        }
    }

    /// <summary>
    /// 线上 NG 或下件，工单更新。
    /// </summary>
    /// <remarks>此方法会统计工单各SN状态再更新，不会出现逻辑上的错误。</remarks>
    /// <param name="wo">工单单号</param>
    /// <param name="snStatus">SN 状态</param>
    /// <param name="isCompleted">是否已完工，只有 OK 状态才能完工</param>
    /// <returns></returns>
    public async Task OnlineIncrementWithFullAsync(string wo, SnStatusEnum snStatus, bool isCompleted)
    {
        var workOrder = await _woRepo.GetFirstAsync(s => s.Wo == wo);
        if (workOrder == null)
        {
            return;
        }

        if (isCompleted)
        {
            // 该产品完工（OK且完工下线），通过工单中数量计算各状态汇总信息。
            var okCount = await _transSnRepo.CountAsync(s => s.Wo == wo && s.Status == SnStatusEnum.OK && s.IsCompelted); // 完工 OK 数
            var ngCount = await _transSnRepo.CountAsync(s => s.Wo == wo && s.Status == SnStatusEnum.NG);
            var scrapCount = await _transSnRepo.CountAsync(s => s.Wo == wo && s.Status == SnStatusEnum.Scrap);

            workOrder.QualifiedQty = okCount;
            workOrder.UnqualifiedQty = ngCount;
            workOrder.ScrapQty = scrapCount;
            workOrder.CompletedQty = okCount + ngCount + scrapCount;

            if (_appConfig.WorkOrder.IsAutoCompleteAndSwitch)
            {
                // 完工数与总数相等时，可关闭单据
                if (workOrder.CompletedQty == workOrder.TotalQty)
                {
                    workOrder.SetStatus(WoStatusEnum.Completed);
                    workOrder.ActualEndTime = DateTime.Now;
                }
            }
        }
        else
        {
            // NG 和或 OK 过站（非完工），都重新更新 NG 数量。
            // NG 出站，需要计算 NG 数量，因为产品可以在同一个工序重复作业，可能重复 NG；
            // OK 出站，产品可能是非返工上件（重复工作），需要减少 NG 数量。
            if (snStatus is SnStatusEnum.NG or SnStatusEnum.OK)
            {
                var ngCount = await _transSnRepo.CountAsync(s => s.Wo == wo && s.Status == SnStatusEnum.NG);
                if (workOrder.UnqualifiedQty == ngCount)
                {
                    return;
                }

                workOrder.UnqualifiedQty = ngCount;
            }
        }

        await _woRepo.UpdateAsync(workOrder, s => new
        {
            s.ActualEndTime,
            s.Status,
            s.LastStatus,
            s.CompletedQty,
            s.QualifiedQty,
            s.UnqualifiedQty,
            s.UpdateTime,
        });
    }

    /// <summary>
    /// 报废，工单更新。
    /// </summary>
    /// <param name="transSn">SN 流转状态</param>
    /// <returns></returns>
    public async Task ScrapIncrementAsync(string wo)
    {
        var workOrder = await _woRepo.GetFirstAsync(s => s.Wo == wo);
        if (workOrder == null)
        {
            return;
        }

        workOrder.ScrapQty += 1;
        workOrder.CompletedQty += 1;

        // 若是设置为自动完工
        if (_appConfig.WorkOrder.IsAutoCompleteAndSwitch)
        {
            if (workOrder.CompletedQty == workOrder.TotalQty)
            {
                workOrder.SetStatus(WoStatusEnum.Completed);
                workOrder.ActualEndTime = DateTime.Now;
            }
        }

        await _woRepo.UpdateAsync(workOrder, s => new
        {
            s.ActualEndTime,
            s.Status,
            s.LastStatus,
            s.CompletedQty,
            s.QualifiedQty,
            s.ScrapQty,
            s.UpdateTime,
        });
    }
}
